#pragma once
#include "common.h"
#include "queue_t.h"
using std::chrono::high_resolution_clock;

class scheduler;

class fiber : public queue_item<0>,
	public queue_item<1>,
	public queue_item<2>,
	public queue_item<3>
{
public:
	enum class status
	{
		e_init,
		e_error,
		e_running,
		e_wait,
		e_blocking,
		e_done
	};
	typedef std::shared_ptr<fiber> ptr_t;
	typedef std::weak_ptr<fiber> wptr_t;
	typedef typename std::multimap<high_resolution_clock::time_point, ptr_t>::iterator waiting_queue_iterator_t;
	typedef typename std::list<ptr_t>::iterator fiber_queue_iterator_t;

	template<typename F, typename ...Args>
	static ptr_t create_fiber(F &&f, Args &&...args)
	{
		return ptr_t(
			new fiber(std::bind(std::forward<F>(f),
				std::placeholders::_1,
				std::forward<Args>(args)...)));
	}
	template<typename F, typename ...Args>
	static ptr_t create_fiber(F &f, Args &...args)
	{
		return ptr_t(new fiber(std::bind(f,args...)));
	}
	void switch_to_fiber()
	{
		scheduler_fiber_handle = GetCurrentFiber();
		if (scheduler_fiber_handle == fiber_handle_)
		{
			throw std::runtime_error("scheduler_fiber_handle == fiber_handle");
		}
		SwitchToFiber(fiber_handle_);
	}
	void yield()
	{
		SwitchToFiber(scheduler_fiber_handle);
	}
	status get_status()
	{
		return status_;
	}
	void set_status(status _status)
	{
		status_ = _status;
	}
	high_resolution_clock::time_point get_schedule_timepoint()
	{
		return schedule_timepoint_;
	}
	void attach_scheduler(scheduler *_scheduler)
	{
		scheduler_ = _scheduler;
	}
	scheduler *get_scheduler()
	{
		return scheduler_;
	}
	void sleep(uint32_t milliseconds)
	{
		schedule_timepoint_ = high_resolution_clock::now() + 
			high_resolution_clock::duration(
				std::chrono::milliseconds(milliseconds));
		status_ = status::e_wait;
		yield();
	}
	void set_waiting_queue_iterator(waiting_queue_iterator_t it)
	{
		waiting_queue_iterator_ = it;
	}
	waiting_queue_iterator_t get_waiting_queue_iterator()
	{
		return waiting_queue_iterator_;
	}
private:
	fiber(std::function<void()> func)
		:func_(std::move(func)),
		status_(status::e_init)
	{
		init();
	}
	void init()
	{
		fiber_handle_ = CreateFiberEx(0,
			0,
			FIBER_FLAG_FLOAT_SWITCH,
			&fiber::routine,
			this);

		if (fiber_handle_ == NULL)
		{
			auto last_error = GetLastError();
			status_ = status::e_error;
			//...
		}
	}
	static void WINAPI routine(void *pParam)
	{
		fiber *self = reinterpret_cast<fiber*>(pParam);
		self->run();
	}
	void run()
	{
		status_ = status::e_running;
		func_();
		status_ = status::e_done;
		SwitchToFiber(scheduler_fiber_handle);
	}
	void *scheduler_fiber_handle;
	void *fiber_handle_;
	std::function<void()> func_;
	status status_;
	high_resolution_clock::time_point schedule_timepoint_;
	scheduler *scheduler_;
	waiting_queue_iterator_t waiting_queue_iterator_;
};